package com.easy.frame.common.redis;

import org.springframework.data.redis.connection.stream.Record;
import org.springframework.data.redis.core.*;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * redis相关操作工具类
 *
 * @author zzf
 * @date 2020/6/2314:13
 */
public class RedisUtil {

    private RedisTemplate<String, String> stringTemplate;

    /**
     * 获取单例对象
     */
    public static RedisUtil getInstance() {
        return Instance.I;
    }

    /**
     * 使用静态内部类方式创建饿汉单例
     */
    private static class Instance {
        private static final RedisUtil I = new RedisUtil();
    }


    public String set(String key, String val) {
        return set(key, val, RedisConstants.DEFAULT_EXPIRE_SECOND);
    }

    public String set(String key, String val, int expire) {
        return set(key, val, expire, TimeUnit.SECONDS);
    }

    public String set(String key, String val, int expire, TimeUnit timeUnit) {
        if (key == null || val == null) {
            return null;
        }
        if (expire < 0) {
            // 失效时间 < 0即永不失效
            getStringTemplate().opsForValue().set(key, val);
        } else {
            getStringTemplate().opsForValue().set(key, val, expire, timeUnit);
        }
        return val;
    }

    /**
     * 替换已存在的值
     */
    public Boolean setIfPresent(String key, String val) {
        return setIfPresent(key, val, 3L, TimeUnit.SECONDS);
    }

    /**
     * 替换已存在的值
     */
    public Boolean hasKey(String key) {
        return getStringTemplate().hasKey(key);
    }


    /**
     * 替换已存在的值
     */
    public Boolean setIfPresent(String key, String val, long expireTime, TimeUnit timeUnit) {
        return getStringTemplate().opsForValue().setIfPresent(key, val, expireTime, timeUnit);
    }

    /**
     * 非空时插入
     */
    public Boolean setIfAbsent(String key, String val) {
        return setIfAbsent(key, val, 3L, TimeUnit.SECONDS);
    }

    /**
     * 非空时插入
     */
    public Boolean setIfAbsent(String key, String val, long expireTime, TimeUnit timeUnit) {
        return getStringTemplate().opsForValue().setIfAbsent(key, val, expireTime, timeUnit);
    }

    /**
     * 批量插入Redis中不存在的数据
     */
    public boolean multiSetIfAbsent(Map<String, String> map) {
        return getStringTemplate().opsForValue().multiSetIfAbsent(map);
    }


    /**
     * 获取数据
     *
     * @param key 键
     * @return 值
     * @author zzf
     * @date 2018/8/4 9:05
     */
    public String get(String key) {
        if (key == null) {
            return null;
        }
        ValueOperations<String, String> valueOperations = getStringTemplate().opsForValue();
        Object val = valueOperations.get(key);
        return val == null ? null : val.toString();
    }

    /**
     * 保存一个Map
     * <p>默认不过期,需要手动设置过期时间!!</p>
     *
     * @param key 键
     * @param map map对象
     * @author zzf
     * @date 9:16 2020/3/17
     */
    public void hSet(String key, Map<String, String> map) {
        RedisTemplate<String, String> redisTemplate = getStringTemplate();
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        hashOperations.putAll(key, map);
    }

    /**
     * 查询指定键的指定字段值
     *
     * @param key 键
     * @return map对象
     * @author zzf
     * @date 9:25 2020/3/17
     */
    public Object hGetField(String key, String fieldKey) {
        RedisTemplate<String, String> redisTemplate = getStringTemplate();
        HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
        return hashOperations.get(key, fieldKey);
    }

    /**
     * 查询指定键的所有数据
     *
     * @param key 键
     * @return map对象
     * @author zzf
     * @date 9:25 2020/3/17
     */
    public Map<String, Object> hGetAll(String key) {
        RedisTemplate<String, String> redisTemplate = getStringTemplate();
        HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
        return hashOperations.entries(key);
    }


    /**
     * 将指定键的指定字段值 +1
     * <p>默认不过期,需要手动设置过期时间!!</p>
     *
     * @param key       键
     * @param fieldName 字段名
     * @return 加完后的值
     * @author zzf
     * @date 9:25 2020/3/17
     */
    public Long hIncr(String key, String fieldName) {
        return hIncr(key, fieldName, 1L);
    }

    /**
     * 将指定键的指定字段值 增加指定值
     * <p>默认不过期,需要手动设置过期时间!!</p>
     *
     * @param key       键
     * @param fieldName 字段名
     * @return 加完后的值
     * @author zzf
     * @date 9:25 2020/3/17
     */
    public Long hIncr(String key, String fieldName, long num) {
        RedisTemplate<String, String> redisTemplate = getStringTemplate();
        HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
        return hashOperations.increment(key, fieldName, num);
    }

    /**
     * 将指定键的指定字段值 增加指定值
     * <p>默认不过期,需要手动设置过期时间!!</p>
     *
     * @param key       键
     * @param fieldName 字段名
     * @return 加完后的值
     * @author zzf
     * @date 9:25 2020/3/17
     */
    public Double hIncr(String key, String fieldName, double num) {
        RedisTemplate<String, String> redisTemplate = getStringTemplate();
        HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
        return hashOperations.increment(key, fieldName, num);
    }

    /**
     * 将指定键的指定字段值 +1
     *
     * @param key 键
     * @author zzf
     * @date 9:25 2020/3/17
     */
    public Long hDelAll(String key) {
        RedisTemplate<String, String> redisTemplate = getStringTemplate();
        HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
        Set<String> keys = hashOperations.keys(key);
        return hashOperations.delete(key, keys.toArray());
    }

    /**
     * 指定键的值加一
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public Long incr(String key) {
        return incr(key, 1L);
    }

    /**
     * 指定键的值减一
     *
     * @author zgy
     * @date 17:15 2020/3/4
     */
    public Long decr(String key) {
        return incr(key, -1L);
    }

    /**
     * 指定键的值减一
     *
     * @author zgy
     * @date 17:15 2020/3/4
     */
    public Long incr(String key, long num) {
        ValueOperations<String, String> valueOperations = getStringTemplate().opsForValue();
        return valueOperations.increment(key, num);
    }

    /**
     * 批量查询
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public List<String> multiGet(List<String> keyList) {
        ValueOperations<String, String> valueOperations = getStringTemplate().opsForValue();
        return valueOperations.multiGet(keyList);
    }

    /**
     * 给指定集合中插入数据
     * <p>
     * 注意这里需要手动设置过期时间
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public Long sAdd(String key, Object val) {
        RedisTemplate<String, String> redisTemplate = getStringTemplate();
        SetOperations<String, String> opsForSet = redisTemplate.opsForSet();
        return opsForSet.add(key, val.toString());
    }

    /**
     * 从集合中移除指定元素
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public Long sRemove(String key, Object val) {
        RedisTemplate<String, String> redisTemplate = getStringTemplate();
        SetOperations<String, String> opsForSet = redisTemplate.opsForSet();
        return opsForSet.remove(key, val.toString());
    }

    /**
     * 从集合中随机移除一个元素并返回
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public String sPop(String key) {
        RedisTemplate<String, String> redisTemplate = getStringTemplate();
        SetOperations<String, String> opsForSet = redisTemplate.opsForSet();
        return opsForSet.pop(key);
    }

    /**
     * 指定值是否是该集合的成员
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public Boolean sIsMember(String key, Object val) {
        SetOperations<String, String> opsForSet = getStringTemplate().opsForSet();
        return opsForSet.isMember(key, val);
    }

    /**
     * 查询指定集合的数量
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public Long sCard(String key) {
        SetOperations<String, String> opsForSet = getStringTemplate().opsForSet();
        return opsForSet.size(key);
    }


    /**
     * 查询指定集合的所有值
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public Set<String> sMembers(String key) {
        SetOperations<String, String> opsForSet = getStringTemplate().opsForSet();
        return opsForSet.members(key);
    }


    /**
     * 插入有序集合
     *
     * @param score 分数/权重  用于确定在集合中的位置
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public boolean zAdd(String key, String value, Double score) {
        ZSetOperations<String, String> opsForZSet = getStringTemplate().opsForZSet();
        return opsForZSet.add(key, value, score);
    }

    /**
     * 批量添加有序集合
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public long zMultiAdd(String key, Set<ZSetOperations.TypedTuple<String>> set) {
        if (ObjectUtils.isEmpty(key) || set == null || set.size() == 0) {
            return 0;
        }
        ZSetOperations<String, String> opsForZSet = getStringTemplate().opsForZSet();
        return opsForZSet.add(key, set);
    }

    /**
     * 查询集合成员数量
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public long zCard(String key) {
        ZSetOperations<String, String> opsForZSet = getStringTemplate().opsForZSet();
        long result;
        try {
            result = opsForZSet.zCard(key);
        } catch (NullPointerException e) {
            return 0;
        }
        return result;
    }

    /**
     * 查询集合最后一个元素
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public String getZLast(String key) {
        Set<String> set = getByRange(key, -1L, -1L);
        if (key == null || "".equals(key)) {
            return null;
        } else {
            return set.iterator().next();
        }
    }

    /**
     * 查询集合第一个元素
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public String getZFirst(String key) {
        Set<String> set = getByRange(key, 1L, 1L);
        if (key == null || "".equals(key)) {
            return null;
        } else {
            return set.iterator().next();
        }
    }

    /**
     * 查询指定索引区间内的数据
     * 默认是正序
     *
     * @param begin 开始的索引位(下标) 支持负数，负数表示倒着数
     * @param end   结束的索引位(下标) 支持负数，负数表示倒着数
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public Set<String> getByRange(String key, long begin, long end) {
        ZSetOperations<String, String> opsForZSet = getStringTemplate().opsForZSet();
        return opsForZSet.range(key, begin, end);
    }

    /**
     * 给集合的指定元素增加对应的分值
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public Double zIncr(String key, String val, long score) {
        ZSetOperations<String, String> opsForZSet = getStringTemplate().opsForZSet();
        return opsForZSet.incrementScore(key, val, score);
    }

    /**
     * 删除集合总所有元素
     *
     * @author zzf
     * @date 14:55 2020/3/3
     */
    public Long zDelAll(String key) {
        ZSetOperations<String, String> opsForZSet = getStringTemplate().opsForZSet();
        return opsForZSet.removeRange(key, 0, -1);
    }


    /**
     * 发送流消息
     *
     * @param streamKey  流通道键值
     * @param msgContext 消息内容
     */
    public void sendStreamSmg(String streamKey, Object msgContext) {
        getStringTemplate().opsForStream().add(Record.of(msgContext).withStreamKey(streamKey));
    }


    /**
     * 删除指定数据
     *
     * @param key 键
     * @return 值
     * @author zzf
     * @date 2018/8/4 9:13
     */
    public String del(String key) {
        if (key == null) {
            return null;
        }
        Object val = getStringTemplate().opsForValue().get(key);
        if (val == null) {
            return null;
        }
        getStringTemplate().delete(key);
        return val.toString();
    }


    /**
     * 删除指定数据
     *
     * @param key 键
     * @author zzf
     * @date 2018/8/4 9:13
     */
    public void delMulti(Collection<String> key) {
        if (key == null || key.size() == 0) {
            return;
        }
        getStringTemplate().delete(key);
    }

    /**
     * 删除指定数据集合
     *
     * @param keys 键
     * @author zzf
     * @date 2018/8/4 9:13
     */
    public void del(Collection<String> keys) {
        getStringTemplate().delete(keys);
    }

    /**
     * 根据参数获取所有的key
     *
     * @param param 参数
     * @return key的Set集合
     * @author zzf
     * @date 2018/8/4 9:13
     */
    public Set<String> getKeys(String param) {
        if (param == null) {
            return new HashSet<>();
        }
        return getStringTemplate().keys(param);

    }

    /**
     * 获取所有的值
     *
     * @return 所有值的LinkedList集合
     * @author zzf
     * @date 2018/8/4 9:18
     */
    public Collection<String> getValues() {
        Set<String> keys = this.getKeys(null);
        List<String> values = new LinkedList<>();
        if (keys == null) {
            return values;
        }
        keys.forEach(s -> values.add(get(s)));
        return values;
    }


    private RedisTemplate<String, String> getStringTemplate() {
        if (stringTemplate == null) {

            stringTemplate = SpringUtil.getBean(StringRedisTemplate.class);
        }
        return stringTemplate;
    }


    //- - - - - - - - - - - - - - - - - - - - -  hash类型 - - - - - - - - - - - - - - - - - - - -

    /**
     * 加入缓存
     *
     * @param key 键
     * @param map 键
     */
    public void add(String key, Map<String, String> map) {
        stringTemplate.opsForHash().putAll(key, map);
    }


    /**
     * 获取所有
     *
     * @return 返回所有的键值信息
     */
    public HashOperations<String, String, String> opsForHash() {
        return stringTemplate.opsForHash();
    }

    /**
     * 获取 key 下的 所有  hashkey 和 value
     *
     * @param key 键
     * @return 返回键值对的所有值<Object, Object>
     */
    public Map<Object, Object> getHashEntries(String key) {
        return stringTemplate.opsForHash().entries(key);
    }

    /**
     * 获取 key 下的 所有  hashkey 和 value
     *
     * @param key 外层键
     * @return 返回键值对的所有值<String, String>
     */
    public Map<String, String> getStringHashEntries(String key) {
        Map<Object, Object> entries = stringTemplate.opsForHash().entries(key);
        Map<String, String> stringMap = new HashMap<>();
        for (Map.Entry<Object, Object> entry : entries.entrySet()) {
            stringMap.put((String) entry.getKey(), (String) entry.getValue());
        }
        return stringMap;
    }

    /**
     * 验证指定 key 下 有没有指定的 hashKey
     *
     * @param key     hash的外层key
     * @param hashKey hash的内层key
     * @return boolean
     */
    public boolean hashKey(String key, String hashKey) {
        return stringTemplate.opsForHash().hasKey(key, hashKey);
    }

    /**
     * 删除指定 hash 的 HashKey
     *
     * @param key      外层key
     * @param hashKeys 数组[]
     * @return 删除成功的 数量
     */
    public Long delete(String key, String... hashKeys) {
        return stringTemplate.opsForHash().delete(key, hashKeys);
    }

    /**
     * 给指定 hash 的 hashkey 做增减操作
     *
     * @param key     外层key
     * @param hashKey 内层key
     * @param number  递增的数值，比如：1或者2
     * @return 返回值为递增后的值
     */
    public Long increment(String key, String hashKey, long number) {
        return stringTemplate.opsForHash().increment(key, hashKey, number);
    }

    /**
     * 给指定 hash 的 hashkey 做增减操作
     *
     * @param key
     * @param hashKey
     * @param number
     * @return
     */
    public Double increment(String key, String hashKey, Double number) {
        return stringTemplate.opsForHash().increment(key, hashKey, number);
    }

    /**
     * 获取 key 下的 所有 hashkey 字段
     *
     * @param key 最外层key
     * @return 所有的内层key集合
     */
    public Set<Object> hashKeys(String key) {
        return stringTemplate.opsForHash().keys(key);
    }

    /**
     * 获取 key 下的 所有 hashKey 字段
     *
     * @param key 对应的最外层key值
     */
    public void delHash(String key) {
        Set<Object> objects = hashKeys(key);
        String[] strings = new String[objects.size()];
        final int[] i = {0};
        objects.forEach(s -> {
            strings[i[0]] = (String) s;
            i[0] = i[0] + 1;
        });
        delete(key, strings);
    }

    /**
     * 获取指定 hash 下面的 键值对 数量
     *
     * @param key 最外层key
     * @return 数量
     */
    public Long hashSize(String key) {
        return stringTemplate.opsForHash().size(key);
    }

}
